﻿#include "OiPCH.hpp"
#include "OiChart.hpp"
#include <QtCharts\QScatterSeries>
#include <QtCharts\QLineSeries>
#include <QtCharts\QSplineSeries>
#include <QtCharts\QValueAxis>
#include <QGraphicsRectItem>

namespace Oi
{
    namespace Appl
    {
        class ChartImpl : public Impl<Chart>
        {
        public:
            void init();
            void exit();

        public:
            QList<Tool*> _toolList;
            double _connectionThreshold = 0.3;
            double _linkingThreshold = 0.3;
            double _noiseThreshold = 8;

            int _toolWidth = 300;
            int _toolHeight = 120;
            int _spacing = 15;

            int _fontSize;
            int _dataViewerInterval = 0;
            bool _displayModeSimple = false;
        };

        Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags)
            : QChart(parent, wFlags)
        {
            this->setTheme(ChartThemeDark);
            QLinearGradient backgroundGradient;
            backgroundGradient.setStart(QPointF(0, 0));
            backgroundGradient.setFinalStop(QPointF(0, 1));
            backgroundGradient.setColorAt(1.0, QColor::fromRgb(32, 32, 32));
            backgroundGradient.setColorAt(1.0, QColor::fromRgb(32, 32, 32));
            backgroundGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
            this->setBackgroundVisible(false);

            this->setPlotAreaBackgroundBrush(backgroundGradient);
            this->setPlotAreaBackgroundVisible(true);
            this->layout()->setContentsMargins(0, 0, 0, 0);
            this->setBackgroundRoundness(0);

            grabGesture(Qt::PinchGesture);
            grabGesture(Qt::PanGesture);
        }

        Chart::~Chart()
        {

        }

        QList<ProfileSeries*> Chart::updateProfile(const LTBase::Contours& lstPoint)
        {
            QList<ProfileSeries*> lstProfileSeries;
            QList<ProfileSeries*> lstNewProfileSeries;
            QList<QtCharts::QAbstractSeries*> lstSeries = this->series();
            for (auto s : lstSeries)
            {
                if (s->type() == OI_PROFILE_SERIES_TYPE)
                {
                    lstProfileSeries.append((ProfileSeries*)s);
                }
            }
            double minX = 100.0, minY = 100.0, maxX = 0.0, maxY = 0.0;
            for (int i = 0; i < lstPoint.size(); i++)
            {
                for (int j = 0; j < lstPoint[i].size(); j++)
                {
                    QPointF pointF = lstPoint[i][j];
                    if (pointF.x() < minX)
                    {
                        minX = pointF.x();
                    }
                    if (pointF.y() < minY)
                    {
                        minY = pointF.x();
                    }
                    if (pointF.x() > maxX)
                    {
                        maxX = pointF.x();
                    }
                    if (pointF.y() > maxY)
                    {
                        maxY = pointF.y();
                    }
                }
            }

            for (int i = 0; i < lstPoint.size(); i++)
            {
                if (i < lstProfileSeries.size())
                {
                    lstProfileSeries.at(i)->replace(lstPoint.at(i));
                    lstNewProfileSeries.append(lstProfileSeries.at(i));
                }
                else
                {
                    ProfileSeries* newContour = new ProfileSeries();
                    newContour->append(lstPoint.at(i));
                    this->addSeries(newContour);
                    newContour->attachAxis(this->axisX());
                    newContour->attachAxis(this->axisY());
                    lstNewProfileSeries.append(newContour);
                }
            }

            for (int i = lstPoint.size(); i < lstProfileSeries.size(); i++)
            {
                this->removeSeries(lstProfileSeries.at(i));
                delete lstProfileSeries.at(i);
            }

            return lstNewProfileSeries;
        }

        QList<DesignSeries*> Chart::updateDesign(const LTBase::Contours& lstPoint)
        {
            QList<DesignSeries*> lstDesignSeries;
            QList<DesignSeries*> lstNewDesignSeries;
            QList<QtCharts::QAbstractSeries*> lstSeries = this->series();
            for (auto s : lstSeries)
            {
                if (s->type() == OI_DESIGN_SERIES_TYPE)
                {
                    lstDesignSeries.append((DesignSeries*)s);
                }
            }

            for (int i = 0; i < lstPoint.size(); i++)
            {
                if (i < lstDesignSeries.size())
                {
                    lstDesignSeries.at(i)->replace(lstPoint.at(i));
                    lstNewDesignSeries.append(lstDesignSeries.at(i));
                }
                else
                {
                    DesignSeries* newContour = new DesignSeries();
                    newContour->append(lstPoint.at(i));
                    this->addSeries(newContour);
                    newContour->attachAxis(this->axisX());
                    newContour->attachAxis(this->axisY());
                    lstNewDesignSeries.append(newContour);
                }
            }

            for (int i = lstPoint.size(); i < lstDesignSeries.size(); i++)
            {
                this->removeSeries(lstDesignSeries.at(i));
                delete lstDesignSeries.at(i);
            }

            return lstNewDesignSeries;
        }

        DesignSeries* Chart::updateDesign(const LTBase::PointCloud& lstPoint)
        {
            QList<QtCharts::QAbstractSeries*> lstSeries = this->series();
            for (auto s : lstSeries)
            {
                if (s->type() == OI_DESIGN_SERIES_TYPE)
                {
                    this->removeSeries(s);
                    delete s;
                }
            }

            DesignSeries* newContour = new DesignSeries();
            newContour->append(lstPoint);
            this->addSeries(newContour);
            newContour->attachAxis(this->axisX());
            newContour->attachAxis(this->axisY());

            return newContour;
        }

        void Chart::removeTools()
        {
            OI_F(Chart);
            for (auto &tool : d->_toolList)
            {
                delete tool;
            }
            d->_toolList.clear();
        }

        void Chart::clearAllSeries()
        {
            QList<QtCharts::QAbstractSeries*> lstSeries = this->series();

            for (int i = 0; i < lstSeries.size(); i++)
            {
                this->removeSeries(lstSeries.at(i));
                delete lstSeries.at(i);
            }
        }

        void Chart::getContourPoints(QList<QPointF>& points, int type)
        {
            points.clear();
            QList<QtCharts::QAbstractSeries *> lstSeries = this->series();
            for (auto series : lstSeries)
            {
                if (series->type() & type)
                {
                    points.append(((QtCharts::QLineSeries*)series)->points());
                }
            }
        }

        void Chart::getContours(QList<QList<QPointF>>& points, int type)
        {
            points.clear();

            QList<QtCharts::QAbstractSeries *> lstSeries = this->series();

            for (auto series : lstSeries)
            {
                if (series->type() & type)
                {
                    points.append(((QtCharts::QLineSeries*)series)->points());
                }
            }
        }

        void Chart::autoRange(double percentage, int type, QPointF center)
        {
            QList<QPointF> points;
            getContourPoints(points, type);

            QPolygonF polygon(points.toVector());
            auto displayRect = polygon.boundingRect();
            auto displayBoxSize = plotArea();

            if (points.size() <= 2)
            {
                axisX()->setRange(0, displayBoxSize.width());
                axisY()->setRange(0, displayBoxSize.height());
                return;
            }

            if (!center.isNull())
            {
                double radiumX = qMax(fabs(displayRect.left() - center.x()), fabs(displayRect.right() - center.x()));
                double radiumY = qMax(fabs(displayRect.top() - center.y()), fabs(displayRect.bottom() - center.y()));;
                displayRect = QRectF(center - QPointF(radiumX, radiumY), center + QPointF(radiumX, radiumY));
            }
            
            double rangeX = displayRect.width() / percentage, rangeY = displayRect.height() / percentage;
            if (displayRect.width() / displayRect.height() >= displayBoxSize.width() / displayBoxSize.height())
            {
                rangeY = rangeX * displayBoxSize.height() / displayBoxSize.width();
            }
            else
            {
                rangeX = rangeY * displayBoxSize.width() / displayBoxSize.height();
            }

            axisX()->setRange(displayRect.center().rx() - rangeX / 2.0, displayRect.center().rx() + rangeX / 2.0);
            axisY()->setRange(displayRect.center().ry() - rangeY / 2.0, displayRect.center().ry() + rangeY / 2.0);

            alignTools();
        }

        void Chart::showSnapShot(LTBase::SnapShot snapshot, QColor profileColor, int profileWidth, QColor designColor, int designWidth, double percentage, int type)
        {
            OI_F(Chart);

            clearAllSeries();
            removeTools();

            if ((snapshot.design.size() != 0) && (snapshot.contour.size() !=0))
            {
                /*cv::Mat transMat = LTBase::match(&(snapshot.contour), &(snapshot.design), 5, 1, 100);

                for (int i = 0; i < snapshot.contour.size(); i++)
                    for (int j = 0; j < snapshot.contour[i].size(); j++)
                        snapshot.contour[i][j] = LTBase::toQPointF(LTBase::affineTransPoint(&transMat, LTBase::toCVPoint2f(snapshot.contour[i][j])));*/
            }

            QList<ProfileSeries*> profile = updateProfile(snapshot.contour);
            QPen pen(profileColor);
            pen.setWidth(profileWidth);
            for (auto s : profile)
            {
                s->setPen(pen);
            }

            QList<DesignSeries*> design = updateDesign(LTBase::pointCloudParser(snapshot.design, d->_connectionThreshold, d->_linkingThreshold, d->_noiseThreshold));
            pen.setColor(designColor);
            pen.setWidth(designWidth);
            for (auto s : design)
            {
                s->setPen(pen);
            }

            autoRange(percentage, type);

            QRectF plotArea = this->plotArea();

            for (auto tool : snapshot.toolList)
            {
                if (tool.type >= LTBase::E_TT_ALIGN_REFERLINE_LEFTMOSTPOINT) continue;
                Tool* toolItem = new Tool(static_cast<QGraphicsItem*>(this), static_cast<QChart*>(this), plotArea.x(), plotArea.y(), 100, 50);
                toolItem->setFontSize(d->_fontSize);
                toolItem->setName(tool.name);
                d->_toolList.append(toolItem);
                switch (tool.type)
                {
                case LTBase::E_TT_OUTSIDECALIPER:
                case LTBase::E_TT_CALIPERPAIR:
                case LTBase::E_TT_AREA:
                    toolItem->setOutput(LTBase::measure(tool, &(snapshot.design)));
                    break;
                case LTBase::E_TT_ERRORMEAN:
                case LTBase::E_TT_ERRORMAX:
                case LTBase::E_TT_ERRORRMS:
                    toolItem->setOutput(LTBase::error(tool, &(snapshot.design), &(snapshot.design)));
                    break;
                default:
                    break;
                }
            }
            alignTools();
        }

        void Chart::measurementSnapShot(LTBase::MeasurementSnapShot snapshot, QColor profileColor, int profileWidth, QColor designColor, int designWidth, double percentage, int type)
        {
            OI_F(Chart);

            clearAllSeries();
            removeTools();

            QList<ProfileSeries*> profile = updateProfile(snapshot.profiles[0]);
            QPen pen(profileColor);
            pen.setWidth(profileWidth);
            for (auto s : profile)
            {
                s->setPen(pen);
            }

            QList<DesignSeries*> design = updateDesign(LTBase::pointCloudParser(snapshot.design, d->_connectionThreshold, d->_linkingThreshold, d->_noiseThreshold));
            pen.setColor(designColor);
            pen.setWidth(designWidth);
            for (auto s : design)
            {
                s->setPen(pen);
            }

            autoRange(percentage, type);

            QRectF plotArea = this->plotArea();

            for (auto output : snapshot.outputList)
            {
                if (output.tool.type >= LTBase::E_TT_ALIGN_REFERLINE_LEFTMOSTPOINT) continue;
                Tool* toolItem = new Tool(static_cast<QGraphicsItem*>(this), static_cast<QChart*>(this), plotArea.x(), plotArea.y(), 100, 50);
                toolItem->setFontSize(d->_fontSize);
                toolItem->setName(output.tool.name);
                d->_toolList.append(toolItem);
                toolItem->setOutput(output);
            }

            alignTools();
        }

        void Chart::onSettingChanged()
        {
            emit settingChanged();
        }

        void Chart::showTemplate(LTBase::SnapShot snapshot, QColor designColor, int designWidth)
        {
            OI_F(Chart);

            clearAllSeries();
            removeTools();

            ToolSeries* flattenArea = new ToolSeries();

            for (auto& tool : snapshot.toolList)
            {
                if (tool.type == LTBase::E_TT_FLATTEN)
                {
                    if (tool.data.size() == 2)
                    {
                        flattenArea->append(tool.data[0]);
                        flattenArea->append(QPointF(tool.data[1].x(), tool.data[0].y()));
                        flattenArea->append(tool.data[1]);
                        flattenArea->append(QPointF(tool.data[0].x(), tool.data[1].y()));
                        flattenArea->append(tool.data[0]);
                    }
                }
            }
            QPen flattenPen(Qt::green);
            flattenPen.setWidth(1);
            flattenArea->setPen(flattenPen);
            addSeries(flattenArea);
            flattenArea->attachAxis(axisX());
            flattenArea->attachAxis(axisY());

            QList<DesignSeries*> design = updateDesign(LTBase::pointCloudParser(snapshot.design, d->_connectionThreshold, d->_linkingThreshold, d->_noiseThreshold));
            QPen pen(designColor , designWidth);
            for (auto s : design)
            {
                s->setPen(pen);
            }

            QRectF plotArea = this->plotArea();

            for (auto tool : snapshot.toolList)
            {
                if (tool.type >= LTBase::E_TT_ALIGN_REFERLINE_LEFTMOSTPOINT) continue;
                Tool* toolItem = new Tool(static_cast<QGraphicsItem*>(this), static_cast<QChart*>(this), plotArea.x(), plotArea.y(), 100, 50);
                QObject::connect(toolItem, &Tool::settingChanged, this, &Chart::onSettingChanged);
                toolItem->setFontSize(d->_fontSize);
                toolItem->setName(tool.name);
                if (d->_dataViewerInterval > 0 && !d->_displayModeSimple)
                {
                    DataViewer* viewer = new DataViewer(qApp->property("mainWindow").value<QWidget*>(), tool.name, d->_dataViewerInterval, tool.minval, tool.maxval, tool.warningUpper, tool.warningLower);
                    QObject::connect(viewer, &DataViewer::settingChanged, this, &Chart::onSettingChanged);
                    toolItem->setDataViewer(viewer);
                }
                d->_toolList.append(toolItem);
                switch (tool.type)
                {
                case LTBase::E_TT_OUTSIDECALIPER:
                case LTBase::E_TT_CALIPERPAIR:
                case LTBase::E_TT_AREA:
                    toolItem->setOutput(LTBase::measure(tool, &(snapshot.design)));
                    break;
                case LTBase::E_TT_ERRORMEAN:
                case LTBase::E_TT_ERRORMAX:
                case LTBase::E_TT_ERRORRMS:
                    toolItem->setOutput(LTBase::error(tool, &(snapshot.design), &(snapshot.design)));
                    break;
                default:
                    break;
                }
            }

            alignTools();
        }

        bool toolLowerThan(Tool* tool1, Tool* tool2)
        {
            if (tool1->latchPoint().y() < tool2->latchPoint().y())
                return true;
            else
                return false;
        }

        bool toolLeftOf(Tool* tool1, Tool* tool2)
        {
            if (tool1->latchPoint().x() < tool2->latchPoint().x())
                return true;
            else
                return false;
        }

        void Chart::alignTools()
        {
            OI_F(Chart);

            QList<Tool*> horizontallyOrderedTools;

            for (auto tool : d->_toolList)
            {
                if (!tool->hasDetailedLabel())
                {
                    continue;
                }
                tool->updateLatchPoint();
                horizontallyOrderedTools.append(tool);
            }

            std::sort(horizontallyOrderedTools.begin(), horizontallyOrderedTools.end(), toolLeftOf);

            QList<Tool*> leftColumn;
            QList<Tool*> rightColumn;

            for (int i = 0; i < std::ceil(horizontallyOrderedTools.size() / 2.0); i++)
            {
                leftColumn.append(horizontallyOrderedTools[i]);
            }
            for (int i = std::ceil(horizontallyOrderedTools.size() / 2.0); i < horizontallyOrderedTools.size(); i++)
            {
                rightColumn.append(horizontallyOrderedTools[i]);
            }

            QRectF plotArea = this->plotArea();

            std::sort(leftColumn.begin(), leftColumn.end(), toolLowerThan);
            std::sort(rightColumn.begin(), rightColumn.end(), toolLowerThan);

            float maxColumnSize = std::max(leftColumn.size(), rightColumn.size());
            float scale = 1;
            if (maxColumnSize * (d->_toolHeight + d->_spacing) > plotArea.height())
            {
                float targetHeight = plotArea.height() / maxColumnSize;
                scale = (targetHeight - d->_spacing) / d->_toolHeight;
            }

            for (int i = 0; i < leftColumn.size(); i++)
            {
                leftColumn[i]->setRect(d->_spacing + plotArea.x(), plotArea.y() + i*(d->_toolHeight * scale + d->_spacing) + d->_toolHeight * scale / 4.0, d->_toolWidth * scale, d->_toolHeight * scale /1.5);
            }

            for (int i = 0; i < rightColumn.size(); i++)
            {
                rightColumn[i]->setRect(plotArea.x() + plotArea.width() - d->_toolWidth * scale - d->_spacing, plotArea.y() + i * (d->_toolHeight * scale + d->_spacing) + d->_toolHeight * scale / 4.0, d->_toolWidth * scale, d->_toolHeight * scale /1.5);
            }
        }

        void Chart::updateTool(LTBase::AlgoOutput & output)
        {
            OI_F(Chart);

            for (auto tool : d->_toolList)
            {
                if (tool->name() == output.tool.name)
                {
                    tool->setOutput(output);
                }
            }
        }

        void Chart::updateDataViewer(LTBase::AlgoOutput& output)
        {
            OI_F(Chart);

            for (auto tool : d->_toolList)
            {
                if (tool->name() == output.tool.name)
                {
                    tool->updateDataviewer(output);
                }
            }
        }

        void Chart::setXRange(float min, float max)
        {
            auto displayBoxSize = plotArea();
            axisX()->setRange(min, max);
            axisY()->setRange(0, (max - min) / displayBoxSize.width() * displayBoxSize.height());
        }

        void Chart::saveSetting(const QString& iniFilePath)
        {
            OI_F(Chart);
            QSettings setting(iniFilePath, QSettings::IniFormat);
            auto axisx = static_cast<QtCharts::QValueAxis*>(axisX());
            auto axisy = static_cast<QtCharts::QValueAxis*>(axisY());
            setting.setValue("xMin", axisx->min());
            setting.setValue("xMax", axisx->max());
            setting.setValue("yMin", axisy->min());
            setting.setValue("yMax", axisy->max());

            for (auto& tool : d->_toolList)
            {
                setting.setValue("DisplayMode/" + tool->name(), int(tool->displayMode()));
                if (tool->displayMode() == DisplayMode::GRAPH || tool->displayMode() == DisplayMode::SIMPLE_GRAPH || tool->displayMode() == DisplayMode::DETAILED_GRAPH)
                {
                    setting.setValue("DataViewerPosX/" + tool->name(), tool->dataViewer()->pos().x());
                    setting.setValue("DataViewerPosY/" + tool->name(), tool->dataViewer()->pos().y());
                    setting.setValue("DataViewerWidth/" + tool->name(), tool->dataViewer()->width());
                    setting.setValue("DataViewerHeight/" + tool->name(), tool->dataViewer()->height());
                }
            }
        }

        void Chart::loadSetting(const QString& iniFilePath)
        {
            OI_F(Chart);
            
            QSettings setting(iniFilePath, QSettings::IniFormat);
            double xmin = setting.value("xMin", std::numeric_limits<double>::quiet_NaN()).toDouble();
            double xmax = setting.value("xMax", std::numeric_limits<double>::quiet_NaN()).toDouble();
            double ymin = setting.value("yMin", std::numeric_limits<double>::quiet_NaN()).toDouble();
            double ymax = setting.value("yMax", std::numeric_limits<double>::quiet_NaN()).toDouble();

            if (!std::isnan(xmin) && !std::isnan(xmax) && !std::isnan(ymin) && !std::isnan(ymax))
            {
                axisX()->setRange(xmin, xmax);
                axisY()->setRange(ymin, (xmax - xmin) / plotArea().width() * plotArea().height() + ymin);
            }
            else
            {
                autoRange(0.5, OI_ALL_TYPE);
            }

            for (auto& tool : d->_toolList)
            {
                int displayMode = setting.value("DisplayMode/" + tool->name(), -1).toInt();
                if (displayMode >= 0)
                {
                    tool->setDisplayMode(DisplayMode(displayMode));

                    if (DisplayMode(displayMode) == DisplayMode::GRAPH || DisplayMode(displayMode) == DisplayMode::SIMPLE_GRAPH || DisplayMode(displayMode) == DisplayMode::DETAILED_GRAPH)
                    {
                        int posX = setting.value("DataViewerPosX/" + tool->name(), -1).toInt();
                        int posY = setting.value("DataViewerPosY/" + tool->name(), -1).toInt();
                        int width = setting.value("DataViewerWidth/" + tool->name(), -1).toInt();
                        int height = setting.value("DataViewerHeight/" + tool->name(), -1).toInt();
                        OiWarning() << width;
                        if (width > 0 && height > 0)
                        {
                            tool->dataViewer()->resize(width, height);
                        }

                        if (posX > 0 && posY > 0)
                        {
                            tool->dataViewer()->move(QPoint(posX, posY));
                        }
                    }
                }
            }
        }

        void Chart::setFontSize(int fontSize)
        {
            OI_F(Chart);

            for (auto tool : d->_toolList)
            {
                tool->setFontSize(fontSize);
            }
        }

        void Chart::setDisplayModeSimple(bool flag)
        {
            OI_F(Chart);
            d->_displayModeSimple = flag;
            for (auto tool : d->_toolList)
            {
                //tool->setDisplayModeSimple(flag);
            }
        }

        void Chart::setConnectionThreshold(double value)
        {
            OI_F(Chart);
            d->_connectionThreshold = value;
        }

        void Chart::setNoiseThreshold(double value)
        {
            OI_F(Chart);
            d->_noiseThreshold = value;
        }

        void Chart::setLinkingThreshold(double value)
        {
            OI_F(Chart);
            d->_linkingThreshold = value;
        }

        void Chart::setDataViewerInterval(int value)
        {
            OI_F(Chart);
            d->_dataViewerInterval = value;
        }

        void Chart::showEvent(QShowEvent* event)
        {
            OI_F(Chart);

            for (auto& tool : d->_toolList)
            {
                tool->showDataViewer();
            }
            QChart::showEvent(event);
        }

        void Chart::hideEvent(QHideEvent* event)
        {
            OI_F(Chart);

            for (auto& tool : d->_toolList)
            {
                tool->hideDataViewer();
            }
            QChart::hideEvent(event);
        }

        bool Chart::event(QEvent* event)
        {
            if (event->type() == QEvent::Gesture)
            {
                QGestureEvent* gevt = static_cast<QGestureEvent*>(event);
                if (QPinchGesture* pinch = dynamic_cast<QPinchGesture*>(gevt->gesture(Qt::PinchGesture)))
                {
                    zoom(pinch->scaleFactor());
                    gevt->accept(pinch);
                    emit settingChanged();
                }
                else if (QPanGesture* pan = dynamic_cast<QPanGesture*>(gevt->gesture(Qt::PanGesture)))
                {
                    scroll(pan->lastOffset().x() - pan->offset().x(), pan->offset().y() - pan->lastOffset().y());
                    gevt->accept(pan);
                    emit settingChanged();
                }
            }

            return QtCharts::QChart::event(event);
        }

        void Chart::rigidTransform(cv::Mat transformation, QPointF transformOrigin, int type)
        {
            removeTools();

            cv::Mat homTransformation = (cv::Mat_<double>(3, 3) << 1, 0, transformOrigin.x(), 0, 1, transformOrigin.y(), 0, 0, 1) * transformation * (cv::Mat_<double>(3, 3) << 1, 0, -transformOrigin.x(), 0, 1, -transformOrigin.y(), 0, 0, 1);

            QList<QtCharts::QAbstractSeries*> lstSeries = this->series();
            for (auto s : lstSeries)
            {
                if (s->type() == type)
                {
                    QList<QPointF> points = ((QtCharts::QLineSeries*)s)->points();
                    for (auto& point : points)
                    {
                        point = LTBase::affineTransPoint(&homTransformation, point);
                    }
                    ((QtCharts::QLineSeries*)s)->replace(points);
                }
            }
        }

        void Chart::rigidTransform(double angle, double tx, double ty, int type, QPointF transformOrigin)
        {
            cv::Mat transformation = (cv::Mat_<double>(3, 3) << std::cos(angle), -std::sin(angle), tx, std::sin(angle), std::cos(angle), ty, 0, 0, 1);
            rigidTransform(transformation, transformOrigin, type);
        }
    }

}
